package com.huahan.auth.filter;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.huahan.auth.domain.Menu;
import com.huahan.auth.exception.BusinessException;
import com.huahan.auth.service.MenuService;
import com.mall.common.constant.CommonConstant;
import com.mall.common.constant.ResultCode;
import com.mall.common.utils.StringStrUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

@Slf4j
public class HuahanCheckHeaderTokenFilter extends OncePerRequestFilter {
  private final TokenStore tokenStore;
  private final List<String> anonymousUrlList;

  @Resource
  private MenuService menuService;

  public HuahanCheckHeaderTokenFilter(TokenStore tokenStore,
                                      List<String> anonymousUrlList) {
    this.tokenStore = tokenStore;
    this.anonymousUrlList = anonymousUrlList;
  }

  @Override
  protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
        FilterChain filterChain) throws ServletException, IOException {
        String accessToken = this.extractToken(request);
        String url = request.getRequestURI();
        // check access_token
        if (StringStrUtils.isNotEmpty(accessToken)) {
          // if (matchStatic(url)){
          if (anonymousUrlList.contains(url) ) {
            OAuth2AccessToken oAuth2AccessToken = tokenStore.readAccessToken(accessToken);
            if (ObjectUtils.allNotNull(oAuth2AccessToken)) {
              filterChain.doFilter(request, response);
              return;
            }

            if (!ObjectUtils.anyNotNull(oAuth2AccessToken)) {
              CheckHeaderTokenRequestWrapper requestWrapper =
                  new CheckHeaderTokenRequestWrapper(request);
              requestWrapper.addHeader(CommonConstant.AUTHORIZATION_PARAM, "");
              filterChain.doFilter(requestWrapper,response);
              return;
            }
          }else {
              // TODO: 2022/5/14   check permission
              if (!havePermission(request)){
                  throw new BusinessException(ResultCode.PERMISSION_ACCESS_DENIED);
              }
          }
       }
      filterChain.doFilter(request, response);
  }


  private String extractToken(HttpServletRequest request) {
    // first check the header...
    String token = extractHeaderToken(request);

    if (token == null) {
     // logger.debug("Token not found in headers. Trying request parameters.");
      token = request.getParameter(OAuth2AccessToken.ACCESS_TOKEN);
      if (token == null) {
      //  logger.debug("Token not found in request parameters.  Not an OAuth2 request.");
          //禁止无token访问
          String method = request.getMethod().toUpperCase();
          if (!"OPTIONS".equals(method)){
              String currentURL = request.getRequestURI();
              if (!matchStatic(currentURL)){
                  throw new BusinessException(ResultCode.NOT_LOGIN);
              }
          }
      } else {
        request.setAttribute(OAuth2AuthenticationDetails.ACCESS_TOKEN_TYPE,
            OAuth2AccessToken.BEARER_TYPE);
      }
    }
    return token;
  }
    private boolean hasBear(HttpServletRequest request) {
        Enumeration<String> headers = request.getHeaders(CommonConstant.AUTHORIZATION_PARAM);
        // typically there is only one (most servers enforce that)
        while (headers.hasMoreElements()) {
            String value = headers.nextElement();
            if ((value.toLowerCase().startsWith(OAuth2AccessToken.BEARER_TYPE.toLowerCase()))) {
                return true;
            }else {
                return false;
            }
        }
        return true;
    }
  private String extractHeaderToken(HttpServletRequest request) {
    Enumeration<String> headers = request.getHeaders(CommonConstant.AUTHORIZATION_PARAM);
    // typically there is only one (most servers enforce that)
    while (headers.hasMoreElements()) {
      String value = headers.nextElement();
      if ((value.toLowerCase().startsWith(OAuth2AccessToken.BEARER_TYPE.toLowerCase()))) {
        String authHeaderValue = value.substring(OAuth2AccessToken.BEARER_TYPE.length()).trim();
        // Add this here for the auth details later. Would be better to change the signature of this method.
        request.setAttribute(OAuth2AuthenticationDetails.ACCESS_TOKEN_TYPE,
            value.substring(0, OAuth2AccessToken.BEARER_TYPE.length()).trim());
        int commaIndex = authHeaderValue.indexOf(',');
        if (commaIndex > 0) {
          authHeaderValue = authHeaderValue.substring(0, commaIndex);
        }
        return authHeaderValue;
      }else {
          return value;
      }
    }
    return null;
  }
    private boolean havePermission(HttpServletRequest request){
        String currentURL = request.getRequestURI();
        if (matchStatic(currentURL) || pathMatcher.match("/pc/**",currentURL) || pathMatcher.match("/esb/api/**",currentURL )
                || pathMatcher.match("/app/**",currentURL)){
            return true;
        }else {
            // 后台发送的请求  需要校验权限
            List<Menu> userPerms = menuService.list(new QueryWrapper<Menu>().eq("user_id", "1"));
            for(Menu menuDTO:userPerms){
                if(null!=menuDTO.getUrl() && currentURL.startsWith(menuDTO.getUrl())){
                    return true;
                }
            }
        }

        return false;
    }
    private final AntPathMatcher pathMatcher = new AntPathMatcher();
    public static final List<String> staticUrlList = new ArrayList<>(16);
    @Override
    public void afterPropertiesSet() throws ServletException {
        super.afterPropertiesSet();
        /*不过滤静态文件，兼容前后端不分离模式*/
        addStaticUrlToMap();
    }
    protected void addStaticUrlToMap() {
        staticUrlList.add("/css/**");
        staticUrlList.add("/js/**");
        staticUrlList.add("/fonts/**");
        staticUrlList.add("/img/**");
        staticUrlList.add("/upload/**");
        staticUrlList.add("/files/**");
        staticUrlList.add("/favicon.ico");
        staticUrlList.add("/getVerify");
        staticUrlList.addAll(anonymousUrlList);
    }
    //排除静态文件
    private boolean matchStatic(String url){
        /*不过滤静态文件，兼容前后端不分离模式*/
        for (String path:staticUrlList) {
            if (pathMatcher.match(path,url)){
                return true;
            }
        }
        return false;
    }
}
